/*
 * @Description: ws | wss connect
 * @Author: cy2020
 * @Date: 2022-02-25 09:36:14
 * @LastEditTime: 2022-02-25 19:10:11
 */
const { v4: uuidv4 } = require('uuid');
const Net = require('net');
var url = require('url');
const {
    getAuthInfo,
    log,
    validatorUrl,
    validatorDeviceName,
    strToHex,
} = require('../utils');

class TCP {
    defaultConfig = {
        deviceName:"",
        connectUrl: '', // tcp服务器地址
        productKeys: ``, //
        deviceSecret: ``,
        connectTimeout: 4000, // 超时时间
        keepalive: 60, // 设备与物联网平台建立保活连接时的心跳间隔时间
        clean: true, // 是否清除session 默认是
    };

    config = {}; // 配置项
    device = null; // tcp对象
    state = false; // tcp 是否正常
    username = '';
    password = '';
    socketId = '';

    constructor(config, callback) {
        // 初始化配置对象
        this.config = { ...this.defaultConfig, ...config };
        this.init(callback);
        this.timout();
        this.connect();
        this.end();
        this.error();
        this.message();
        // 发起心跳包
        this.ping(); 
        // 发送设备信息
        this.setDeviceInfo();
    }

    init(callback) {
        let {
            connectUrl, // tcp服务器地址
            deviceName, // 设备信息
            productKeys, //
            deviceSecret,
        } = this.config;
        if (deviceName == '' || deviceName == null) {
            throw new Error('缺少设备名称');
        }
        if (connectUrl == '' || connectUrl == null) {
            throw new Error('缺少服务器地址');
        }
        if (productKeys == '' || productKeys == null) {
            throw new Error('缺少productKeys');
        }
        if (deviceSecret == '' || deviceSecret == null) {
            throw new Error('缺少deviceSecret');
        }
        const result = validatorUrl(connectUrl);
        if (!result) {
            throw new Error('url不合法');
        }
        const isValid = validatorDeviceName(deviceName);
        if (!isValid) {
            throw new Error('设备名字非法');
        }
        let authInfo = {};
        if (callback != void 0) {
            authInfo = callback(deviceName, productKeys, deviceSecret);
        } else {
            authInfo = getAuthInfo(deviceName, deviceSecret);
        }
        if (authInfo == void 0) {
            throw new Error('authInfo 错误');
        }
        if (authInfo.username == void 0 || authInfo.username == '') {
            throw new Error('username 错误');
        }
        if (authInfo.password == void 0 || authInfo.password == '') {
            throw new Error('password 错误');
        }
        this.username = authInfo.username;
        this.password = authInfo.password;
        var q = url.parse(connectUrl,true);
        const options = {
            host:q.hostname,
            port:q.port,
        };
        log.info('获取了设备信息[{}]', options);
        this.device = new Net.Socket();
        // 长连接
        this.device.setKeepAlive(true);
        this.device.connect(options,callback);
    }

    reconnect() {
        //当重新连接启动触发回调
        this.device.on('reconnect', function(){
            log.info('mqtt正在重连.....');
        });
    }

    timout() {
        let _that = this;
        //当 socket 空闲超时时触发，仅是表明 socket 已经空闲。用户必须手动关闭连接。
        this.device.on('timout', function () {
            _that.state = false;
            log.warn('tcp-client客户端脱机下线.....');
        });
    }

    error() {
        let _that = this;
        //当客户端无法连接或出现错误时触发回调
        this.device.on('error', function (error) {
            _that.state = false;
            log.error('tcp-client客户端出现错误[{}]:', error);
        });
    }
   
    connect() {
        let _that = this;
        //成功连接后触发的回调
        this.device.on('connect', function (connack) {
            // tcp 能正常通信
            _that.state = true;
            log.info('tcp-client成功连接上服务器可以正常通信了');
        });
    }
   
    /**
     * 关闭tcp
     */
    end() {
        //当 socket 完全关闭时触发。参数 had_error 是布尔值，它表示是否因为传输错误导致 socket 关闭。
        this.device.on('end', function (connack) {
            // tcp 能正常通信
            this.state = true;
            log.info('tcp-client关闭');
        });
    }
    //发布数据
    publish(
        topic,
        data,
        callback
    ) {
        data.topic = topic;
        data = JSON.stringify(data)
        log.info('客户端发布了数据[{}]:', data);
        this.device.write(data+"\n", callback);
    }

    //当客户端接收到发布消息时触发回调
    message() {
        let _that = this;
        this.device.on('data', function(message) {
            let msg = message.toString('utf8');
            /**
             * public class DeviceMessageReply {
             * 
             * //与元数据中定义的类型一致,如果是对象类型,请转为java.util.HashMap
             * public String data;
             * 
             * // 消息的唯一标识,用于在请求响应模式下对请求和响应进行关联.
             * private String messageId
             * // 时间戳
             * private long timestamp;
             * 
             * //主题
             * public  String topic;
             * 
             * //当前设备连接会话ID 能找到对应的endport
             * private String sessionId;
             * 
             * // 设备号
             * public  String deviceId;
             */
            let DeviceMessageReply = JSON.parse(msg)
            console.log(DeviceMessageReply)
            if (DeviceMessageReply.topic == "SOCKETID") {
                _that.socketId = DeviceMessageReply.sessionId;
            }
            log.info(
                '客户端【接收】订阅消息消息数据:[{}]',
                message.toString('utf8')
            );
        });
    }
    
    ping(){
        // 发起心跳包- 
        let _that = this;
        setInterval(() => {
             // 发送客户端信息
             let ping = {
                data: {} ,
                messageId: uuidv4(),
                timestamp: new Date().getTime(),
                sessionId: _that.socketId,
                deviceId: _that.config.deviceName
            }
            _that.publish("PING", ping,()=>{})
        }, 20000);
    }

    setDeviceInfo(){
        let _that = this;
        setTimeout(() => {
             // 发送客户端信息
            let DEVICEINFO = {
                data: {
                    username: _that.username,
                    password: _that.password,
                } ,
                messageId: uuidv4(),
                timestamp: new Date().getTime(),
                sessionId: _that.socketId,
                deviceId: _that.config.deviceName
            }
            _that.publish("DEVICEINFO",DEVICEINFO,()=>{})
        }, 200);
    }

}

module.exports = TCP;
